
#include "include/font.h"

#include "assert.h"

#include "printf.h"

#include "vfs_spiffs.h"

/*
 * Name Envy-Code-B-10pt-1.ttf (8 * 10)
 * Included characters:
 *  !"#$%&'()*+,-./0123456789:;<=>?\x0040ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~
 * Preprocess:
 *  main scan direction: top_to_bottom
 *  line scan direction: forward
 */
static uint8_t ascii[] = {
	/* 0x20 [ ] */
		0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	/* 0x21 [!] */
		0x00,0x10,0x10,0x10,0x10,0x10,0x10,0x00,0x10,0x10,0x00,0x00,
	/* 0x22 ["] */
		0x00,0x48,0x48,0x48,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	/* 0x23 [#] */
		0x00,0x00,0x00,0x28,0x7C,0x28,0x28,0x7C,0x28,0x00,0x00,0x00,
	/* 0x24 [$] */
		0x00,0x10,0x3C,0x50,0x50,0x30,0x18,0x14,0x14,0x78,0x10,0x00,
	/* 0x25 [%] */
		0x00,0x00,0x30,0x49,0x32,0x04,0x08,0x10,0x26,0x49,0x06,0x00,
	/* 0x26 [&] */
        0x00,0x00,0x78,0x48,0x50,0x66,0xA4,0x94,0x88,0x76,0x00,0x00,
	/* 0x27 ['] */
		0x00,0x10,0x10,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	/* 0x28 [(] */
		0x08,0x10,0x10,0x20,0x20,0x20,0x20,0x20,0x20,0x10,0x10,0x08,
	/* 0x29 [)] */
		0x20,0x10,0x10,0x08,0x08,0x08,0x08,0x08,0x08,0x10,0x10,0x20,
	/* 0x2A [*] */
		0x00,0x10,0x54,0x38,0x54,0x10,0x00,0x00,0x00,0x00,0x00,0x00,
	/* 0x2B [+] */
		0x00,0x00,0x00,0x10,0x10,0x7C,0x10,0x10,0x00,0x00,0x00,0x00,
	/* 0x2C [,] */
		0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x30,0x00,
	/* 0x2D [-] */
		0x00,0x00,0x00,0x00,0x00,0x7C,0x00,0x00,0x00,0x00,0x00,0x00,
	/* 0x2E [.] */
		0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x30,0x00,0x00,
	/* 0x2F [/] */
		0x00,0x04,0x04,0x08,0x08,0x10,0x10,0x20,0x20,0x40,0x40,0x00,
	/* 0x30 [0] */
		0x00,0x38,0x44,0x44,0x4C,0x54,0x64,0x44,0x44,0x38,0x00,0x00,
	/* 0x31 [1] */
		0x00,0x10,0x30,0x10,0x10,0x10,0x10,0x10,0x10,0x38,0x00,0x00,
	/* 0x32 [2] */
		0x00,0x38,0x44,0x04,0x08,0x10,0x20,0x40,0x40,0x7C,0x00,0x00,
	/* 0x33 [3] */
		0x00,0x38,0x44,0x04,0x04,0x18,0x04,0x04,0x44,0x38,0x00,0x00,
	/* 0x34 [4] */
		0x00,0x08,0x18,0x28,0x28,0x48,0x48,0x7C,0x08,0x08,0x00,0x00,
	/* 0x35 [5] */
		0x00,0x7C,0x40,0x40,0x40,0x78,0x04,0x04,0x04,0x78,0x00,0x00,
	/* 0x36 [6] */
		0x00,0x08,0x10,0x20,0x40,0x78,0x44,0x44,0x44,0x38,0x00,0x00,
	/* 0x37 [7] */
		0x00,0x7C,0x04,0x04,0x08,0x08,0x10,0x10,0x20,0x20,0x00,0x00,
	/* 0x38 [8] */
		0x00,0x38,0x44,0x44,0x44,0x38,0x44,0x44,0x44,0x38,0x00,0x00,
	/* 0x39 [9] */
		0x00,0x38,0x44,0x44,0x44,0x38,0x08,0x10,0x20,0x40,0x00,0x00,
	/* 0x3A [:] */
		0x00,0x00,0x00,0x00,0x30,0x30,0x00,0x00,0x30,0x30,0x00,0x00,
	/* 0x3B [;] */
		0x00,0x00,0x00,0x00,0x30,0x30,0x00,0x00,0x30,0x30,0x60,0x00,
	/* 0x3C [<] */
		0x00,0x00,0x08,0x10,0x20,0x40,0x20,0x10,0x08,0x00,0x00,0x00,
	/* 0x3D [=] */
		0x00,0x00,0x00,0x00,0xFC,0x00,0xFC,0x00,0x00,0x00,0x00,0x00,
	/* 0x3E [>] */
		0x00,0x00,0x40,0x20,0x10,0x08,0x10,0x20,0x40,0x00,0x00,0x00,
	/* 0x3F [?] */
		0x00,0x38,0x44,0x44,0x04,0x08,0x10,0x00,0x10,0x10,0x00,0x00,
	/* 0x40 [@] */
		0x00,0x38,0x44,0x82,0x9A,0xAA,0xAA,0xAA,0xAA,0x9C,0x80,0x40,
	/* 0x41 [A] */
		0x00,0x38,0x44,0x44,0x44,0x7C,0x44,0x44,0x44,0x44,0x00,0x00,
	/* 0x42 [B] */
		0x00,0x78,0x44,0x44,0x44,0x78,0x44,0x44,0x44,0x78,0x00,0x00,
	/* 0x43 [C] */
		0x00,0x1C,0x20,0x40,0x40,0x40,0x40,0x40,0x20,0x1C,0x00,0x00,
	/* 0x44 [D] */
		0x00,0x70,0x48,0x44,0x44,0x44,0x44,0x44,0x48,0x70,0x00,0x00,
	/* 0x45 [E] */
		0x00,0x7C,0x40,0x40,0x40,0x78,0x40,0x40,0x40,0x7C,0x00,0x00,
	/* 0x46 [F] */
		0x00,0x7C,0x40,0x40,0x40,0x78,0x40,0x40,0x40,0x40,0x00,0x00,
	/* 0x47 [G] */
		0x00,0x1C,0x20,0x40,0x40,0x4C,0x44,0x44,0x24,0x1C,0x00,0x00,
	/* 0x48 [H] */
		0x00,0x44,0x44,0x44,0x44,0x7C,0x44,0x44,0x44,0x44,0x00,0x00,
	/* 0x49 [I] */
		0x00,0x7C,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x7C,0x00,0x00,
	/* 0x4A [J] */
		0x00,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x44,0x38,0x00,0x00,
	/* 0x4B [K] */
		0x00,0x44,0x44,0x48,0x50,0x60,0x50,0x48,0x44,0x44,0x00,0x00,
	/* 0x4C [L] */
		0x00,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x7C,0x00,0x00,
	/* 0x4D [M] */
		0x00,0x44,0x44,0x6C,0x6C,0x54,0x54,0x44,0x44,0x44,0x00,0x00,
	/* 0x4E [N] */
		0x00,0x44,0x44,0x64,0x64,0x54,0x54,0x4C,0x4C,0x44,0x00,0x00,
	/* 0x4F [O] */
		0x00,0x38,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x38,0x00,0x00,
	/* 0x50 [P] */
		0x00,0x78,0x44,0x44,0x44,0x78,0x40,0x40,0x40,0x40,0x00,0x00,
	/* 0x51 [Q] */
		0x00,0x38,0x44,0x44,0x44,0x44,0x44,0x44,0x48,0x34,0x04,0x00,
	/* 0x52 [R] */
		0x00,0x78,0x44,0x44,0x44,0x78,0x50,0x48,0x44,0x44,0x00,0x00,
	/* 0x53 [S] */
		0x00,0x3C,0x40,0x40,0x20,0x10,0x08,0x04,0x04,0x78,0x00,0x00,
	/* 0x54 [T] */
		0x00,0x7C,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x00,0x00,
	/* 0x55 [U] */
		0x00,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x38,0x00,0x00,
	/* 0x56 [V] */
		0x00,0x44,0x44,0x44,0x44,0x28,0x28,0x28,0x10,0x10,0x00,0x00,
	/* 0x57 [W] */
		0x00,0x44,0x44,0x44,0x44,0x44,0x54,0x54,0x54,0x28,0x00,0x00,
	/* 0x58 [X] */
		0x00,0x44,0x44,0x28,0x28,0x10,0x28,0x28,0x44,0x44,0x00,0x00,
	/* 0x59 [Y] */
		0x00,0x44,0x44,0x44,0x28,0x28,0x10,0x10,0x10,0x10,0x00,0x00,
	/* 0x5A [Z] */
		0x00,0x7C,0x04,0x04,0x08,0x10,0x20,0x40,0x40,0x7C,0x00,0x00,
	/* 0x5B [[] */
		0x38,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x38,
	/* 0x5C [\] */
		0x00,0x40,0x40,0x20,0x20,0x10,0x10,0x08,0x08,0x04,0x04,0x00,
	/* 0x5D []] */
		0x38,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x38,
	/* 0x5E [^] */
		0x00,0x10,0x10,0x28,0x28,0x44,0x00,0x00,0x00,0x00,0x00,0x00,
	/* 0x5F [_] */
		0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7E,0x00,
	/* 0x60 [`] */
		0x00,0x30,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	/* 0x61 [a] */
		0x00,0x00,0x00,0x38,0x04,0x04,0x3C,0x44,0x44,0x3C,0x00,0x00,
	/* 0x62 [b] */
		0x40,0x40,0x40,0x78,0x44,0x44,0x44,0x44,0x44,0x78,0x00,0x00,
	/* 0x63 [c] */
		0x00,0x00,0x00,0x1C,0x20,0x40,0x40,0x40,0x20,0x1C,0x00,0x00,
	/* 0x64 [d] */
		0x04,0x04,0x04,0x3C,0x44,0x44,0x44,0x44,0x44,0x3C,0x00,0x00,
	/* 0x65 [e] */
		0x00,0x00,0x00,0x38,0x44,0x44,0x7C,0x40,0x40,0x3C,0x00,0x00,
	/* 0x66 [f] */
		0x00,0x1C,0x20,0x20,0x78,0x20,0x20,0x20,0x20,0x20,0x00,0x00,
	/* 0x67 [g] */
		0x00,0x00,0x00,0x3C,0x44,0x44,0x44,0x44,0x3C,0x04,0x04,0x7C,
	/* 0x68 [h] */
		0x00,0x40,0x40,0x58,0x64,0x44,0x44,0x44,0x44,0x44,0x00,0x00,
	/* 0x69 [i] */
		0x00,0x10,0x00,0x30,0x10,0x10,0x10,0x10,0x10,0x10,0x00,0x00,
	/* 0x6A [j] */
		0x00,0x08,0x00,0x18,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x30,
	/* 0x6B [k] */
		0x00,0x40,0x40,0x44,0x48,0x50,0x60,0x50,0x48,0x44,0x00,0x00,
	/* 0x6C [l] */
		0x00,0x30,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x00,0x00,
	/* 0x6D [m] */
		0x00,0x00,0x00,0x68,0x54,0x54,0x54,0x54,0x54,0x54,0x00,0x00,
	/* 0x6E [n] */
		0x00,0x00,0x00,0x58,0x64,0x44,0x44,0x44,0x44,0x44,0x00,0x00,
	/* 0x6F [o] */
		0x00,0x00,0x00,0x38,0x44,0x44,0x44,0x44,0x44,0x38,0x00,0x00,
	/* 0x70 [p] */
		0x00,0x00,0x00,0x78,0x44,0x44,0x44,0x44,0x44,0x78,0x40,0x40,
	/* 0x71 [q] */
		0x00,0x00,0x00,0x3C,0x44,0x44,0x44,0x44,0x44,0x3C,0x04,0x04,
	/* 0x72 [r] */
		0x00,0x00,0x00,0x58,0x64,0x40,0x40,0x40,0x40,0x40,0x00,0x00,
	/* 0x73 [s] */
		0x00,0x00,0x00,0x3C,0x40,0x40,0x38,0x04,0x04,0x78,0x00,0x00,
	/* 0x74 [t] */
		0x00,0x20,0x20,0x78,0x20,0x20,0x20,0x20,0x20,0x1C,0x00,0x00,
	/* 0x75 [u] */
		0x00,0x00,0x00,0x44,0x44,0x44,0x44,0x44,0x4C,0x34,0x00,0x00,
	/* 0x76 [v] */
		0x00,0x00,0x00,0x44,0x44,0x44,0x28,0x28,0x10,0x10,0x00,0x00,
	/* 0x77 [w] */
		0x00,0x00,0x00,0x44,0x44,0x44,0x54,0x54,0x54,0x28,0x00,0x00,
	/* 0x78 [x] */
		0x00,0x00,0x00,0x44,0x44,0x28,0x10,0x28,0x44,0x44,0x00,0x00,
	/* 0x79 [y] */
		0x00,0x00,0x00,0x44,0x44,0x44,0x28,0x28,0x28,0x10,0x10,0x20,
	/* 0x7A [z] */
		0x00,0x00,0x00,0x7C,0x04,0x08,0x10,0x20,0x40,0x7C,0x00,0x00,
	/* 0x7B [{] */
		0x06,0x08,0x08,0x08,0x10,0x60,0x10,0x08,0x08,0x08,0x08,0x06,
	/* 0x7C [|] */
		0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,
	/* 0x7D [}] */
		0x60,0x10,0x10,0x10,0x08,0x06,0x08,0x10,0x10,0x10,0x10,0x60,
	/* 0x7E [~] */
		0x00,0x00,0x00,0x00,0x24,0x54,0x48,0x00,0x00,0x00,0x00,0x00,
};

struct font
{
  /* data */
  uint8_t width;
  uint8_t high;
  uint8_t index;
  uint8_t source;
  void *this;
} font_config = {
  8, 12, ASCII, BuildIn, ascii
};

static inline void font_init(uint8_t width, uint8_t high, uint8_t index, uint8_t source_type, void *font_offset)
{
  struct font tmp = { width, high, index, source_type, font_offset};
  font_config = tmp;
}

#include "vfs_wrapper.h"
#include "nlr.h"

void font_free()
{
  switch (font_config.index)
  {
    case UTF8:
    case Unicode:
        if (font_config.source == FileIn)
        {
            file_close(font_config.this);
        }
    case GBK:
    case GB2312:
    case ASCII:
    default:
      font_init(8, 12, ASCII, BuildIn, ascii);
      break;
  }
}

void font_load(uint8_t index, uint8_t width, uint8_t high, uint8_t source_type, void *src_addr)
{

    switch (index)
    {
    case UTF8:
        if (src_addr == NULL)
        {
            font_init(8, 12, ASCII, BuildIn, ascii);
            break;
        }
        font_init(width, high, UTF8, source_type, src_addr);
    break;
    default:
    case Unicode:
    case GBK:
    case GB2312:
    case ASCII:
        font_init(8, 12, ASCII, BuildIn, ascii);
    break;
    }
}

int font_get_utf8_size(const uint8_t pInput)
{
   uint8_t c = pInput;
   // 0xxxxxxx 1
   // 10xxxxxx -1
   // 110xxxxx 2
   // 1110xxxx 3
   // 11110xxx 4
   // 111110xx 5
    // 1111110x 6
    if(c< 0x80) return 1;
    if(c>=0x80 && c<0xC0) return -1;
    if(c>=0xC0 && c<0xE0) return 2;
    if(c>=0xE0 && c<0xF0) return 3;
    if(c>=0xF0 && c<0xF8) return 4;
    if(c>=0xF8 && c<0xFC) return 5;
    if(c>=0xFC) return 6;
    return 0;
}

int font_utf8_to_unicode(const uint8_t* pInput, uint64_t *Unic)
{  
    assert(pInput != NULL && Unic != NULL);

    char b1, b2, b3, b4, b5, b6;

    *Unic = 0x0;
    int utfbytes = font_get_utf8_size(*pInput);
    uint8_t *pOutput = (uint8_t *) Unic;

    switch ( utfbytes )
    {  
        case 1:  
            *pOutput     = *pInput;
            break;  
        case 2:  
            b1 = *pInput;
            b2 = *(pInput + 1);
            if ( (b2 & 0xE0) != 0x80 )
                return 0;  
            *pOutput     = (b1 << 6) + (b2 & 0x3F);
            *(pOutput+1) = (b1 >> 2) & 0x07;
            break;
        case 3:
            b1 = *pInput;
            b2 = *(pInput + 1);
            b3 = *(pInput + 2);
            if ( ((b2 & 0xC0) != 0x80) || ((b3 & 0xC0) != 0x80) )
                return 0;
            *pOutput     = (b2 << 6) + (b3 & 0x3F);
            *(pOutput+1) = (b1 << 4) + ((b2 >> 2) & 0x0F);
            break;
        case 4:
            b1 = *pInput;
            b2 = *(pInput + 1);
            b3 = *(pInput + 2);
            b4 = *(pInput + 3);
            if ( ((b2 & 0xC0) != 0x80) || ((b3 & 0xC0) != 0x80)
                    || ((b4 & 0xC0) != 0x80) )
                return 0;
            *pOutput     = (b3 << 6) + (b4 & 0x3F);
            *(pOutput+1) = (b2 << 4) + ((b3 >> 2) & 0x0F);
            *(pOutput+2) = ((b1 << 2) & 0x1C)  + ((b2 >> 4) & 0x03);
            break;
        case 5:
            b1 = *pInput;
            b2 = *(pInput + 1);
            b3 = *(pInput + 2);
            b4 = *(pInput + 3);
            b5 = *(pInput + 4);
            if ( ((b2 & 0xC0) != 0x80) || ((b3 & 0xC0) != 0x80)
                    || ((b4 & 0xC0) != 0x80) || ((b5 & 0xC0) != 0x80) )
                return 0;
            *pOutput     = (b4 << 6) + (b5 & 0x3F);
            *(pOutput+1) = (b3 << 4) + ((b4 >> 2) & 0x0F);
            *(pOutput+2) = (b2 << 2) + ((b3 >> 4) & 0x03);
            *(pOutput+3) = (b1 << 6);
            break;
        case 6:
            b1 = *pInput;
            b2 = *(pInput + 1);
            b3 = *(pInput + 2);
            b4 = *(pInput + 3);
            b5 = *(pInput + 4);
            b6 = *(pInput + 5);
            if ( ((b2 & 0xC0) != 0x80) || ((b3 & 0xC0) != 0x80)
                    || ((b4 & 0xC0) != 0x80) || ((b5 & 0xC0) != 0x80)
                    || ((b6 & 0xC0) != 0x80) )
                return 0;
            *pOutput     = (b5 << 6) + (b6 & 0x3F);
            *(pOutput+1) = (b5 << 4) + ((b6 >> 2) & 0x0F);
            *(pOutput+2) = (b3 << 2) + ((b4 >> 4) & 0x03);
            *(pOutput+3) = ((b1 << 6) & 0x40) + (b2 & 0x3F);
            break;
        default:
            return 0;
            break;
    }  

    return utfbytes;
}

void imlib_draw_font(image_t *img, int x_off, int y_off, int c, float scale, uint8_t font_h, uint8_t font_w, const uint8_t *font)
{
    // It will be replaced by *.SVG.
    /* font ↑ ↓ ↝ →
        01 02 03 04 
        05 06 07 08
        09 10 11 12
        13 14 15 16
        1. ↓ 01 05 09 13
        2. → 01 02 03 04
        3. Got it?
    */
    for (int y = 0, yy = fast_roundf(font_h * scale); y < yy; y++) {
        uint8_t pos = fast_roundf(y / scale);
        uint32_t tmp = font[pos];
        for (uint8_t i = 1; i < font_h / 8; i++) {
            tmp <<= 8, tmp |= font[pos + i * font_h];
        }
        for (uint8_t x = 0, xx = fast_roundf(font_w * scale); x < xx; x++) {
            if (tmp & (1 << (font_w - 1 - fast_roundf(x / scale)))) {
                imlib_set_pixel(img, (x_off + x), (y_off + y), c);
            }
        }
    }
}

// def draw_string(img, x, y, c, s, string, width, high, fonts, space=1):
//     i = 0
//     pos = 0
//     while i < len(string):
//         utfbytes = encode_get_utf8_size(string[i])
//         print(i, string[i], utfbytes, string[i:i+utfbytes])
//         tmp = encode_utf8_to_unicode(string[i:i+utfbytes])
//         i += utfbytes
//         pos += 1
//         fonts.seek(tmp * int(high*(width/8)))
//         img.draw_font(x + (pos * s * (width + space)), y, width, high, fonts.read(int(high*(width/8))), scale=s, color=c)

void imlib_draw_utf8_string(image_t *img, int x_off, int y_off, mp_obj_t str, int c, float scale, int x_spacing, int y_spacing, bool mono_space) {
  const uint8_t font_len = (font_config.width / 8) * font_config.high;
  const uint8_t *string = mp_obj_str_get_str(str);
  int len = mp_obj_len(str);

  // mp_buffer_info_t bufinfo;
  // mp_get_buffer_raise(str, &bufinfo, MP_BUFFER_READ);

  for(int i = 0, pos = 0, bytes = 0; i < len; i += bytes, pos += 1) {

    uint64_t offset = 0;
    bytes = font_utf8_to_unicode(string + i, &offset);
    // printk("utfbytes %d offset %llu\r\n", bytes, offset);
    if (bytes <= 0 || offset <= 0) { // unicode len
      break;
    }
    
    // printk("offset %llX\r\n", offset);

    uint8_t buffer[font_len];
    
    switch (font_config.source)
    {
        case FileIn:
            file_seek_raise(font_config.this, offset * font_len, 0);
            read_data_raise(font_config.this, buffer, font_len);
            break;
        case ArrayIn:
            // printk("%d %p %p %p", font_len, buffer, font_config.this, &font_config.this[offset * font_len]);
            // memcpy(buffer, &font_config.this[offset * font_len], font_len);
            sys_spiffs_read(font_config.this + offset * font_len, font_len, buffer);
            break;
        default:
            break;
    }

    const uint8_t *font = buffer;

    if (!mono_space) {
        // Find the first pixel set and offset to that.
        bool exit = false;

        for (int x = 0, xx = font_config.width; x < xx; x++) {
            for (int y = 0, yy = font_config.high; y < yy; y++) {
                if (font[y] & (1 << (font_config.width - 1 - x))) {
                    x_off -= fast_roundf(x * scale);
                    exit = true;
                    break;
                }
            }

            if (exit) break;
        }
    }

    imlib_draw_font(img, x_off, y_off, c, scale, font_config.high, font_config.width, font);

    if (mono_space) {
        x_off += fast_roundf(font_config.width * scale) + x_spacing;
    } else {
        // Find the last pixel set and offset to that.
        bool exit = false;

        for (int x = font_config.width - 1; x >= 0; x--) {
            for (int y = font_config.high - 1; y >= 0; y--) {
                if (font[y] & (1 << (font_config.width - 1 - x))) {
                    x_off += fast_roundf((x + 2) * scale) + x_spacing;
                    exit = true;
                    break;
                }
            }

            if (exit) break;
        }

        if (!exit) x_off += fast_roundf(scale * 3); // space char
    }
  }
}

void imlib_draw_ascii_string(image_t *img, int x_off, int y_off, const char *str, int c, float scale, int x_spacing, int y_spacing, bool mono_space) {
    const uint8_t font_len = (font_config.width / 8) * font_config.high;
    const int anchor = x_off;

    for(char ch, last = '\0'; (ch = *str); str++, last = ch) {

        if ((last == '\r') && (ch == '\n')) { // handle "\r\n" strings
            continue;
        }

        if ((ch == '\n') || (ch == '\r')) { // handle '\n' or '\r' strings
            x_off = anchor;
            y_off += fast_roundf(font_config.high * scale) + y_spacing; // newline height == space height
            continue;
        }

        if ((ch < ' ') || (ch > '~')) { // handle unknown characters
            imlib_draw_rectangle(img,
                x_off + (fast_roundf(scale * 3) / 2),
                y_off + (fast_roundf(scale * 3) / 2),
                fast_roundf(font_config.high * scale) - ((fast_roundf(scale * 3) / 2) * 2),
                fast_roundf(font_config.width * scale) - ((fast_roundf(scale * 3) / 2) * 2),
                c, fast_roundf(scale), false);
            continue;
        }

        const uint8_t *font = &ascii[(ch - ' ') * font_len];
        
        if (!mono_space) {
            // Find the first pixel set and offset to that.
            bool exit = false;

            for (int x = 0, xx = font_config.width; x < xx; x++) {
                for (int y = 0, yy = font_config.high; y < yy; y++) {
                    if (font[y] & (1 << (font_config.width - 1 - x))) {
                        x_off -= fast_roundf(x * scale);
                        exit = true;
                        break;
                    }
                }

                if (exit) break;
            }
        }

        imlib_draw_font(img, x_off, y_off, c, scale, font_config.high, font_config.width, font);

        if (mono_space) {
            x_off += fast_roundf(font_config.width * scale) + x_spacing;
        } else {
            // Find the last pixel set and offset to that.
            bool exit = false;

            for (int x = font_config.width - 1; x >= 0; x--) {
                for (int y = font_config.high - 1; y >= 0; y--) {
                    if (font[y] & (1 << (font_config.width - 1 - x))) {
                        x_off += fast_roundf((x + 2) * scale) + x_spacing;
                        exit = true;
                        break;
                    }
                }

                if (exit) break;
            }

            if (!exit) x_off += fast_roundf(scale * 3); // space char
        }
    }
}

inline void imlib_draw_string(image_t *img, int x_off, int y_off, mp_obj_t str, int c, float scale, int x_spacing, int y_spacing, bool mono_space)
{
  // 检查字库文件是否有效
  mp_obj_base_t* fs_info = (mp_obj_base_t*)(font_config.this);
  if(font_config.source == FileIn && fs_info->type->protocol == NULL){
    font_init(8, 12, ASCII, BuildIn, ascii);
  }
  if (font_config.index == ASCII) {
    const char *arg_str = mp_obj_str_get_str(str);
    imlib_draw_ascii_string(img, x_off, y_off, arg_str, c, scale, x_spacing, y_spacing, mono_space);
  } else if (font_config.index == UTF8) {
    imlib_draw_utf8_string(img, x_off, y_off, str, c, scale, x_spacing, y_spacing, mono_space);
  }
}